package org.example;

import lombok.extern.slf4j.Slf4j;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.*;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 代码生成器
 * 1. generate()方法入参: 表名数组
 * 2. 执行generate后, 实体类被替换, mapper.java被替换, mapper.xml更新;
 * 3. 因为mapper.java会被替换掉, 推荐提交完所有代码后重新生成实体, 可直接恢复之前代码;
 */
@Slf4j
public class CodeGenerator {
    //JDBC配置
    private static final String JDBC_DIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
    private static final String JDBC_USERNAME = "root";
    private static final String JDBC_PASSWORD = "81568156";
    private static final String JDBC_URL = "jdbc:mysql://localhost:3306/lysh?serverTimezone=UTC";
    //项目在硬盘上的基础路径
    private static final String PROJECT_PATH = System.getProperty("user.dir") + "/mybatis-tkMybatis";
    //JAVA文件路径
    //private static final String JAVA_PATH = "/src/test/java";
    private static final String JAVA_PATH = "/src/main/java";
    //资源文件路径
    //private static final String RESOURCES_PATH = "/src/test/resources";
    private static final String RESOURCES_PATH = "/src/main/resources";
    //项目基础包名称
    private static final String BASE_PACKAGE = "org.example";

    //Model所在包
    private static final String ENTITY_PACKAGE = BASE_PACKAGE + ".entity";
    //Mapper所在包
    private static final String MAPPER_PACKAGE = BASE_PACKAGE + ".repository";
    //Mapper插件基础接口的完全限定名
    private static final String MAPPER_INTERFACE_REFERENCE = BASE_PACKAGE + ".base.TkMapper";

    /**
     * 下划线正则
     */
    private static final Pattern linePattern = Pattern.compile("_(\\w)");


    public static void main(String[] args) {
        CodeGenerator.generate(
                "employees"
        );
    }


    public static void generate(String... tableNames) {
        for (String tableName : tableNames) {
            CodeGenerator.genModelAndMapper(tableName);
        }
    }


    public static void genModelAndMapper(String tableName) {
        Context context = new Context(ModelType.FLAT);
        context.setId("Potato");
        context.setTargetRuntime("MyBatis3Simple");
        context.addProperty(PropertyRegistry.CONTEXT_BEGINNING_DELIMITER, "`");
        context.addProperty(PropertyRegistry.CONTEXT_ENDING_DELIMITER, "`");
        context.addProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING, "UTF-8");
        //JDBC
        JDBCConnectionConfiguration jdbcConnectionConfiguration = new JDBCConnectionConfiguration();
        jdbcConnectionConfiguration.setConnectionURL(JDBC_URL);
        jdbcConnectionConfiguration.setUserId(JDBC_USERNAME);
        jdbcConnectionConfiguration.setPassword(JDBC_PASSWORD);
        jdbcConnectionConfiguration.setDriverClass(JDBC_DIVER_CLASS_NAME);
        context.setJdbcConnectionConfiguration(jdbcConnectionConfiguration);
        //实体类
        JavaModelGeneratorConfiguration javaModelGeneratorConfiguration = new JavaModelGeneratorConfiguration();
        javaModelGeneratorConfiguration.setTargetProject(PROJECT_PATH + JAVA_PATH);
        javaModelGeneratorConfiguration.setTargetPackage(ENTITY_PACKAGE);
        //javaModelGeneratorConfiguration.addProperty(PropertyRegistry.ANY_CONSTRUCTOR_BASED, "true");
        javaModelGeneratorConfiguration.addProperty(PropertyRegistry.ANY_ROOT_CLASS, "org.example.base.BaseEntity");
        context.setJavaModelGeneratorConfiguration(javaModelGeneratorConfiguration);
        //Comment
        CommentGeneratorConfiguration commentGeneratorConfiguration = new CommentGeneratorConfiguration();
        //commentGeneratorConfiguration.addProperty(PropertyRegistry.COMMENT_GENERATOR_DATE_FORMAT, "yyyy-MM-dd HH:mm:ss");
        commentGeneratorConfiguration.addProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_ALL_COMMENTS, "true");
        commentGeneratorConfiguration.addProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_DATE, "true");
        context.setCommentGeneratorConfiguration(commentGeneratorConfiguration);
        //Type
        JavaTypeResolverConfiguration javaTypeResolverConfiguration = new JavaTypeResolverConfiguration();
        javaModelGeneratorConfiguration.addProperty(PropertyRegistry.TYPE_RESOLVER_FORCE_BIG_DECIMALS, "true");
        context.setJavaTypeResolverConfiguration(javaTypeResolverConfiguration);
        //XML-MAPPER 目前使用TK通用mapper 可以选择不生成XML文件
        SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration = new SqlMapGeneratorConfiguration();
        sqlMapGeneratorConfiguration.setTargetProject(PROJECT_PATH + RESOURCES_PATH);
        sqlMapGeneratorConfiguration.setTargetPackage("mappers");
        context.setSqlMapGeneratorConfiguration(sqlMapGeneratorConfiguration);
        //Repository(此处不设置TK父接口, 使用TK插件实现)
        JavaClientGeneratorConfiguration javaClientGeneratorConfiguration = new JavaClientGeneratorConfiguration();
        javaClientGeneratorConfiguration.setTargetProject(PROJECT_PATH + JAVA_PATH);
        javaClientGeneratorConfiguration.setTargetPackage(MAPPER_PACKAGE);
        javaClientGeneratorConfiguration.setConfigurationType("XMLMAPPER");
        context.setJavaClientGeneratorConfiguration(javaClientGeneratorConfiguration);
        //Table
        TableConfiguration tableConfiguration = new TableConfiguration(context);
        tableConfiguration.setTableName(tableName);
        tableConfiguration.setGeneratedKey(new GeneratedKey("id", "Mysql", true, null));
        //并不好用的插件
        //DomainObjectRenamingRule domainObjectRenamingRule = new DomainObjectRenamingRule();
        //domainObjectRenamingRule.setSearchString("Tbl");
        //domainObjectRenamingRule.setReplaceString("");
        //tableConfiguration.setDomainObjectRenamingRule(domainObjectRenamingRule);
        tableConfiguration.setDomainObjectName(lineToHump(tableName));
        tableConfiguration.setMapperName(lineToHump(tableName) + "Mapper");
        context.addTableConfiguration(tableConfiguration);
        //插件
        PluginConfiguration pluginConfiguration01 = new PluginConfiguration();
        pluginConfiguration01.setConfigurationType("org.example.TkMapperPlugin");
        pluginConfiguration01.addProperty("mappers", MAPPER_INTERFACE_REFERENCE);
        pluginConfiguration01.addProperty("needMapperAnnotation", "true");
        pluginConfiguration01.addProperty("needsAllArgsConstructor", "false");
        context.addPluginConfiguration(pluginConfiguration01);
        //取消添加序列化, 转为继承父类实现
        //PluginConfiguration pluginConfiguration02 = new PluginConfiguration();
        //pluginConfiguration02.setConfigurationType("org.mybatis.generator.plugins.SerializablePlugin");
        //context.addPluginConfiguration(pluginConfiguration02);

        List<String> warnings;
        MyBatisGenerator generator;
        try {
            Configuration config = new Configuration();
            config.addContext(context);
            config.validate();
            DefaultShellCallback callback = new DefaultShellCallback(true);
            warnings = new ArrayList<>();
            generator = new MyBatisGenerator(config, callback, warnings);
            generator.generate(null);
        } catch (Exception e) {
            throw new RuntimeException("生成Model和Mapper失败", e);
        }

        if (generator.getGeneratedJavaFiles().isEmpty() || generator.getGeneratedXmlFiles().isEmpty()) {
            throw new RuntimeException("生成Model和Mapper失败：" + warnings);
        }

        log.info("生成成功-{}-", tableName);
    }


    /**
     * 下划线转驼峰
     * -- 主要是插件不好用
     */
    public static String lineToHump(String str) {
        if (str.startsWith("tbl")) {
            str = str.replace("tbl", "");
        }
        if (str.startsWith("rel")) {
            str = str.replace("rel", "");
            str = str + "_r";
        }
        str = str.toLowerCase();
        str = str.substring(0, 1).toUpperCase() + str.substring(1);
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }


}
